﻿#region Using Statements
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using System.Xml;
using GameBase.Storage;
#endregion

namespace StarFox
{
    /// <summary>
    /// Basic functionality needed for score ranking management.
    /// </summary>
    public class GameScore
    {
        #region Singleton Pattern

        // the only instance of this class... eager initialization
        static readonly GameScore instance = new GameScore();

        // private constructor
        private GameScore()
        {
        }

        /// <summary>
        /// Gets a common instance of this class.
        /// </summary>
        public static GameScore Instance
        {
            get { return instance; }
        }

        #endregion

        #region Game Information

        // backing fields
        int lives;
        int score;

        /// <summary>
        /// Gets or sets the current number of lives available.
        /// </summary>
        public int Lives
        {
            get { return lives; }
            set { lives = Clamp(value, 0, 99); }
        }

        /// <summary>
        /// Gets or sets the score currently achieved.
        /// </summary>
        public int Score
        {
            get { return score; }
            set { score = Clamp(value, 0, 999999999); }
        }

        /// <summary>
        /// Gets or sets the level distance currently covered.
        /// </summary>
        public float LevelPosition { get; set; }

        /// <summary>
        /// Gets or sets the current elapsed game time.
        /// </summary>
        public TimeSpan PlayTime { get; set; }

        /// <summary>
        /// Gets or sets the name of the player.
        /// </summary>
        public string Name { get; set; }

        private int Clamp(int value, int min, int max)
        {
            value = (value > max) ? max : value;
            value = (value < min) ? min : value;
            return value;
        }

        #endregion

        #region Score Management

        /// <summary>
        /// Stores information associated to players in the ranking.
        /// </summary>
        private struct HighScore
        {
            // backing fields
            string name;
            int score;

            /// <summary>
            /// Gets the name of the player.
            /// </summary>
            public string Name { get { return name; } }

            /// <summary>
            /// Gets the score achieved by the player.
            /// </summary>
            public int Score { get { return score; } }

            /// <summary>
            /// Initializes a new instance of this struct.
            /// </summary>
            /// <param name="name">Name of the player.</param>
            /// <param name="score">The score achieved by the player.</param>
            public HighScore(string name, int score)
            {
                this.name = name;
                this.score = score;
            }
        }

        /// <summary>
        /// Sets a new score in the ranking.
        /// </summary>
        public void SetScore()
        {
            int i = 0;

            while (i < ranking.Count)
            {
                if (ranking[i].Name == Name)
                    break;
                
                i++;
            }

            if (i < ranking.Count)
            {
                if (ranking[i].Score > Score)
                    return;

                ranking.RemoveAt(i);

                for (int j = 0; j < ranking.Count; j++)
                {
                    if (ranking[j].Score < Score)
                    {
                        ranking.Insert(j, new HighScore(Name, Score));
                        return;
                    }
                }

                ranking.Insert(ranking.Count - 1, new HighScore(Name, Score));
            }
            else
            {
                for (int j = 0; j < ranking.Count; j++)
                {
                    if (ranking[j].Score < Score)
                    {
                        ranking.RemoveAt(ranking.Count - 1);
                        ranking.Insert(j, new HighScore(Name, Score));
                        return;
                    }
                }
            }
        }

        /// <summary>
        /// Gets the current ranking.
        /// </summary>
        /// <param name="playerNames">Names of the players in the ranking.</param>
        /// <param name="playerScores">Scores of the players in the ranking.</param>
        public void GetScore(out string[] playerNames, out int[] playerScores)
        {
            playerNames = new string[ranking.Count];
            for (int i = 0; i < ranking.Count; i++)
                playerNames[i] = ranking[i].Name;

            playerScores = new int[ranking.Count];
            for (int i = 0; i < ranking.Count; i++)
                playerScores[i] = ranking[i].Score;
        }

        // list initially empty...
        readonly List<HighScore> ranking = new List<HighScore>(5);

        #endregion

        #region Storage Management

        // initialize storage manager
        StorageManager storage = StorageManager.Instance;

        // container and file where to load or save game settings
        string scoresContainer = "AppData";
        string scoresFile = "Scores.xml";

        #region Load Scores

        // scores loaded correctly
        bool isLoadOk;

        /// <summary>
        /// Loads the score ranking.
        /// </summary>
        public void LoadScores()
        {
            // game scores loaded correctly
            isLoadOk = false;

            storage.StorageOperation(scoresContainer,
                (container) =>
                {
                    if (container.FileExists(scoresFile))
                    {
                        using (Stream stream = container.OpenFile(scoresFile, FileMode.Open))
                        {
                            using (XmlReader reader = XmlReader.Create(stream))
                            {
                                #region Scores

                                while (reader.Read())
                                {
                                    if (reader.NodeType == XmlNodeType.Element && reader.Name == "highscores")
                                    {
                                        isLoadOk = true;
                                        break;
                                    }
                                }

                                if (!isLoadOk)
                                    return;
                                else
                                    isLoadOk = false;

                                #endregion

                                #region Players

                                int itemCount = 0;

                                while (reader.Read())
                                {
                                    if (reader.NodeType == XmlNodeType.Element && reader.Name == "item")
                                    {
                                        if (reader["player"] == null || reader["score"] == null)
                                            return;

                                        try
                                        {
                                            ranking.Add(new HighScore(reader["player"], Convert.ToInt32(reader["score"])));
                                        }
                                        catch
                                        {
                                            return;
                                        }

                                        if (++itemCount == 5)
                                        {
                                            isLoadOk = true;
                                            break;
                                        }
                                    }
                                }

                                #endregion
                            }
                        }
                    }
                });

            if (!isLoadOk)
            {
                // error... generate default list
                ranking.Clear();
                ranking.Add(new HighScore("FATHOM", 17500));
                ranking.Add(new HighScore("VIPER", 15000));
                ranking.Add(new HighScore("NONI", 12500));
                ranking.Add(new HighScore("MODJO", 10000));
                ranking.Add(new HighScore("TUOMAS", 7500));
            }
        }

        #endregion

        #region Save Scores

        /// <summary>
        /// Saves the score ranking.
        /// </summary>
        public void SaveScores()
        {
            storage.StorageOperation(scoresContainer,
                (container) =>
                {
                    using (Stream stream = container.OpenFile(scoresFile, FileMode.Create))
                    {
                        XmlWriterSettings settings = new XmlWriterSettings();
                        settings.ConformanceLevel = ConformanceLevel.Document;
                        settings.Indent = true;
                        settings.CloseOutput = true;
                        settings.CheckCharacters = true;

                        using (XmlWriter writer = XmlWriter.Create(stream, settings))
                        {
                            writer.WriteStartDocument();
                            writer.WriteComment(" " + Application.ProductVersion + " ");
                            writer.WriteComment(" HIGHSCORES ");
                            writer.WriteComment(" do not manually edit this file ");
                            writer.WriteStartElement("highscores");

                            foreach (HighScore score in ranking)
                            {
                                writer.WriteStartElement("item");
                                writer.WriteAttributeString("player", score.Name);
                                writer.WriteAttributeString("score", score.Score.ToString());
                                writer.WriteEndElement();
                            }

                            writer.WriteEndElement();
                            writer.WriteEndDocument();
                            writer.Flush();
                        }
                    }
                });
        }

        #endregion

        #endregion
    }
}
